package com.tbit.uqbike.service.business.tag.aop;

import com.tbit.uqbike.object.pojo.vo.BatchOperationMsgVO;
import com.tbit.uqbike.service.business.tag.event.TagDeleteSuccessEvent;
import com.tbit.uqbike.service.business.tag.event.TagDisableSuccessEvent;
import com.tbit.uqbike.webmanager.dto.TagDTO;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * 标签操作切面
 *
 *
 * @author <a href="mailto:chen.chaoxiong@tbit.com.cn">ChenChaoXiong</a>
 * @since 2024/7/15
 */
@Aspect
@Component
@Slf4j
public class TagOperationAspect {

    @Autowired
    private ApplicationEventPublisher eventPublisher;

    @Around("execution(* com.tbit.uqbike.service.business.tag.TagService.disableTag(..))")
    public Object aroundDisableTag(ProceedingJoinPoint joinPoint) throws Throwable {
       return process(joinPoint, OperationType.DISABLE);


    }

    @Around("execution(* com.tbit.uqbike.service.business.tag.TagService.deleteTag(..))")
    public Object aroundDeleteTag(ProceedingJoinPoint joinPoint) throws Throwable {
        return process(joinPoint, OperationType.DELETE);
    }

    @Around("execution(* com.tbit.uqbike.service.business.tag.TagService.disableBatch(..))")
    public Object aroundDisableBatch(ProceedingJoinPoint joinPoint) throws Throwable {
        return process(joinPoint, OperationType.BATCH_DISABLE);
    }


    @Around("execution(* com.tbit.uqbike.service.business.tag.TagService.deleteTagBatch(..))")
    public Object aroundDeleteTagBatch(ProceedingJoinPoint joinPoint) throws Throwable {
        return process(joinPoint, OperationType.BATCH_DELETE);
    }

    private Object process(ProceedingJoinPoint joinPoint, OperationType operationType) throws Throwable {
        Object result = null;
        try {
            result = joinPoint.proceed();
            asyncOperation(result, joinPoint, operationType);
            return result;
        } catch (Throwable throwable) {
            log.error("Error during {} operation", operationType.name().toLowerCase(), throwable);
            throw throwable;
        }
    }


    @Async
    public void asyncOperation(Object result, ProceedingJoinPoint joinPoint, OperationType operationType) {
        switch (operationType) {
            case DISABLE:
                sendDisableSuccessEvent(result, joinPoint);
                break;
            case DELETE:
                sendDeleteSuccessEvent(result, joinPoint);
                break;
            case BATCH_DISABLE:
                sendDisableBatchSuccessEvent(result, joinPoint);
                break;
            case BATCH_DELETE:
                sendDeleteBatchSuccessEvent(result, joinPoint);
                break;
            default:
                break;
        }
    }




    private void sendDeleteSuccessEvent(Object result, ProceedingJoinPoint joinPoint) {
        if (!(result instanceof Boolean)) {
            log.error("Expected Boolean result but received {}", result.getClass().getSimpleName());
            return;
        }

        Object[] args = joinPoint.getArgs();
        if (args.length < 1) {
            log.error("[sendDeleteSuccessEvent] Method argument is missing.");
            return;
        }

        if (!(args[0] instanceof TagDTO)) {
            log.error("[sendDeleteSuccessEvent] Method argument is not an instance of TagDTO.");
            return;
        }

        TagDTO tag = (TagDTO) args[0];
        Boolean success = (Boolean) result;
        if (success) {
            eventPublisher.publishEvent(new TagDeleteSuccessEvent(this, tag));
        } else {
            log.info("Tag {} delete failed.", tag.getId());
        }
    }

    private void sendDisableSuccessEvent(Object result, ProceedingJoinPoint joinPoint) {
        if (!(result instanceof Boolean)) {
            log.error("Expected Boolean result but received {}", result.getClass().getSimpleName());
            return;
        }

        Object[] args = joinPoint.getArgs();
        if (args.length < 1) {
            log.error("[sendDisableSuccessEvent] Method argument is missing.");
            return;
        }

        if (!(args[0] instanceof TagDTO)) {
            log.error("[sendDisableSuccessEvent] Method argument is not an instance of TagDTO.");
            return;
        }

        TagDTO tag = (TagDTO) args[0];
        Boolean success = (Boolean) result;
        if (success) {
            eventPublisher.publishEvent(new TagDisableSuccessEvent(this, tag));
        } else {
            log.info("Tag {} disable failed.", tag.getId());
        }
    }

    private void sendDisableBatchSuccessEvent(Object result, ProceedingJoinPoint joinPoint) {
        if (!(result instanceof BatchOperationMsgVO)) {
            log.error("Expected BatchOperationMsgVO result but received {}", result.getClass().getSimpleName());
            return;
        }

        BatchOperationMsgVO<String> batchOperationMsgVO = (BatchOperationMsgVO<String>) result;
        List<String> successValues = batchOperationMsgVO.getSuccessList();
        if (successValues.isEmpty()) {
            log.info("No successful operations to process.");
            return;
        }

        Object[] args = joinPoint.getArgs();
        if (args.length < 1 || !(args[0] instanceof List) || ((List<?>) args[0]).isEmpty() || !(((List<?>) args[0]).get(0) instanceof TagDTO)) {
            log.error("Method argument is missing or not an instance of List<TagDTO>.");
            return;
        }

        List<TagDTO> tags = (List<TagDTO>) args[0];
        tags.stream()
            .filter(tag -> successValues.contains(tag.getId().toString()))
            .forEach(tag -> eventPublisher.publishEvent(new TagDisableSuccessEvent(this, tag)));
    }

    private void sendDeleteBatchSuccessEvent(Object result, ProceedingJoinPoint joinPoint) {
        if (!(result instanceof BatchOperationMsgVO)) {
            log.error("Expected BatchOperationMsgVO result but received {}", result.getClass().getSimpleName());
            return;
        }

        BatchOperationMsgVO<String> batchOperationMsgVO = (BatchOperationMsgVO<String>) result;
        List<String> successValues = batchOperationMsgVO.getSuccessList();
        if (successValues.isEmpty()) {
            log.info("No successful delete operations to process.");
            return;
        }

        Object[] args = joinPoint.getArgs();
        if (args.length < 1 || !(args[0] instanceof List) || ((List<?>) args[0]).isEmpty() || !(((List<?>) args[0]).get(0) instanceof TagDTO)) {
            log.error("Method argument is missing or not an instance of List<TagDTO>.");
            return;
        }

        List<TagDTO> tags = (List<TagDTO>) args[0];
        tags.stream()
            .filter(tag -> successValues.contains(tag.getId().toString()))
            .forEach(tag -> eventPublisher.publishEvent(new TagDeleteSuccessEvent(this, tag)));
    }


    private enum OperationType {
        DISABLE, DELETE, BATCH_DISABLE, BATCH_DELETE
    }


}